package gitee.derumin.lifeissohard.lishhungersystem;

import gitee.derumin.lifeissohard.criteria.LishCriteria;
import gitee.derumin.lifeissohard.item.ImagedCake;
import gitee.derumin.lifeissohard.tools.CustomDamageSources;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.player.HungerManager;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.world.Difficulty;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;
import java.util.UUID;

public class LishHungerManager extends HungerManager {

    public static final String NUTRIENTS = "nutritionLevel";
    public static final String APPLY_UPDATE = "applyUpdate";
    public static final String IS_INPUTTING = "is_inputting";
    public static final String IS_INPUTTING = "is_inputting";
    public static final String WEAK_TIME = "weakTime";
    public static final String ENERGY_BALANCE = "energyBalance";
    public static final String EXERCISE_HISTORY = "exerciseHistory";
    public static final String DIGEST_DIFFICULTY = "digestDifficulty";
    public static final String WATER = "water";
    public static final String UNDIGESTED_WATER = "undigestedWater";
    public static final String HEAVY_METAL = "heavyMetal";
    public static final String BLOOD_SUGAR = "bloodSugar";
    public static final String STORED_SUGAR = "storedSugar";
    public static final String UNDIGESTED_SUGAR = "undigestedSugar";
    public static final String BLOOD_FAT = "bloodFat";
    public static final String STORED_FAT= "storedFat";
    public static final String UNDIGESTED_FAT = "undigestedFat";
    public static final String MUSCLE = "muscle";
    public static final String PROTEIN = "protein";
    public static final String UNDIGESTED_PROTEIN = "undigestedProtein";
    public static final String CELLULOSE = "cellulose";
    public static final String VA = "vitaminA";
    public static final String VB = "vitaminB";
    public static final String VC = "vitaminC";
    public static final String IODINE = "iodine";
    private static final float SUGAR_HEAT_RECIPROCAL = 12500f;
    private static final int FOOD_LEVEL_EQUALS_SUGAR = 25000;
    private static final UUID TEMP_ATTACK_DAMAGE_ID = UUID.fromString("8960ba3e-aee6-49be-bb41-b7db53734e0b");

    private int foodLevel = 18 ;
    private float saturationLevel = 0f;
    private float exhaustion = 0f;
    private int foodTickTimer = 0;
    private int prevFoodLevel = 18;
    private boolean applyUpdate = false;
    private boolean isInputting = false;
    private int weakTime = 0;
    private double energyBalance = 450000d;
    private double exerciseHistory = 14400d;
    private double digestDifficulty = 0d;
    private double water = 36000000d;
    private double undigestedWater = 0d;
    private double heavyMetal = 0d;
    private double bloodSugar = 3780d;
    private double storedSugar = 450000d;
    private double undigestedSugar = 0d;
    private double bloodFat = 21450d;
    private double storedFat = 12000000d;
    private double undigestedFat = 0d;
    private double muscle = 8000000d;
    private double protein = 4000000d;
    private double undigestedProtein = 0d;
    private double cellulose = 1000d;
    private double vitaminA = 800d;
    private double vitaminB = 120d;
    private double vitaminC = 1000d;
    private double iodine = 150d;

    public LishHungerManager(){
    }

    @Override
    public void add(int food, float saturationModifier) {
        if (this.applyUpdate){
            this.storedSugar +=  food * 25000 * (2 * saturationModifier + 1);
            this.energyBalance +=  food * 25000 * (2 * saturationModifier + 1);
        }
    }

    @Override
    public void eat(Item item, @Nullable ItemStack stack) {
        if (this.applyUpdate) {
            LishFoodComponent lishFoodComponent = item.getFoodComponent() instanceof LishFoodComponent lishFoodComponent1? lishFoodComponent1 : LishFoods.getLishFoodComponent(item.toString());
            if (lishFoodComponent != null) this.addNutrient(lishFoodComponent);
        }
    }

    public void drink(String name){
        if (this.applyUpdate) {
            LishFoodComponent lishFoodComponent = LishFoods.getLishFoodComponent(name);
            if (lishFoodComponent != null) this.addNutrient(lishFoodComponent);
        }
    }

    private void addNutrient(LishFoodComponent lishFoodComponent){
        double containAdd = lishFoodComponent.getSugar()+lishFoodComponent.getFat()+lishFoodComponent.getProtein();
        if (containAdd > 1d)this.digestDifficulty = (this.getStomachDryContains()*this.digestDifficulty + containAdd*lishFoodComponent.getDifficulty())/(this.getStomachDryContains()+containAdd);
        if (this.digestDifficulty > 0.9d)this.digestDifficulty = 0.9d;
        if (this.digestDifficulty < 0d)this.digestDifficulty = 0d;
        this.undigestedWater += lishFoodComponent.getWater();
        this.heavyMetal += lishFoodComponent.getHeavyMetal();
        this.undigestedSugar += lishFoodComponent.getSugar();
        this.undigestedFat += lishFoodComponent.getFat();
        this.undigestedProtein += lishFoodComponent.getProtein();
        this.cellulose += lishFoodComponent.getCellulose();
        if (this.cellulose > 10000d)this.cellulose = 10000d;
        this.vitaminA += lishFoodComponent.getVa();
        if (this.vitaminA > 8000d)this.vitaminA = 8000d;
        this.vitaminB += lishFoodComponent.getVb();
        if (this.vitaminB > 1200d)this.vitaminB = 1200d;
        this.vitaminC += lishFoodComponent.getVc();
        if (this.vitaminC > 10000d)this.vitaminC = 10000d;
        this.iodine += lishFoodComponent.getIodine();
        if (this.iodine > 900d)this.iodine = 900d;
    }

    @Override
    public void update(PlayerEntity player){
        if (player instanceof ServerPlayerEntity) {
            int gameMode = ((ServerPlayerEntity)player).interactionManager.getGameMode().getId();
            this.applyUpdate = gameMode == 0 || gameMode == 2;
            if (this.applyUpdate) {
                NutritionEffects nutritionEffects = new NutritionEffects();
                Difficulty difficulty = player.world.getDifficulty();
                this.prevFoodLevel = this.foodLevel;
                this.exerciseHistory -= this.exerciseHistory / 5000000d;
                double stomachContains = this.getStomachContains();
                if (this.exhaustion > 0f) {
                    float cost = Math.min(this.exhaustion / 20f + 0.001f, this.exhaustion);
                    this.exhaustion -= cost;
                    this.exerciseHistory += cost;
                    this.bloodSugar -= SUGAR_HEAT_RECIPROCAL * cost;
                    if (Math.random() * cost * stomachContains > 1000000d && !player.isDead()) {
                        player.damage(CustomDamageSources.STOMACH_RUPTURE, Float.MAX_VALUE);
                        if (player.isDead()) {
                            nutritionEffects.dieFromStomachRupture = true;
                        }
                    }
                } else this.exhaustion = 0f;
                this.bloodSugar -= Math.sqrt(Math.max(this.iodine + 200d, 200d) / 350d) * (this.muscle + (Math.min(this.protein, 4000000d))) / (player.getSleepTimer() == 0 ? 480000d : 960000d);
                if (stomachContains > 2000000d && !player.isDead()) {
                    player.damage(CustomDamageSources.GLUTTONY, Float.MAX_VALUE);
                    if (player.isDead()) {
                        nutritionEffects.dieFromGluttony = true;
                    }
                }
                if (stomachContains > 1000000d) {
                    nutritionEffects.isFull = true;
                } else if (stomachContains < 1d) {
                    nutritionEffects.isEmptyStomach = true;
                }
                this.cellulose -= this.cellulose / 100000d;
                double cakeAddition = (player.getInventory().getMainHandStack().getItem() instanceof ImagedCake ? 1d : 0d) + (player.getInventory().getStack(PlayerInventory.OFF_HAND_SLOT).getItem() instanceof ImagedCake ? 1d : 0d);
                if (this.undigestedWater > 0d) {
                    double digest = Math.min(this.undigestedWater / 800d + 150d, this.undigestedWater);
                    this.undigestedWater -= digest;
                    this.water += digest;
                } else this.undigestedWater = 0d;
                if (this.undigestedSugar > 0d) {
                    double digest = Math.min((Math.sqrt(Math.max(Math.min(this.cellulose, 4000d), 0d)) * 3d + 35d) * (1d - this.digestDifficulty) + cakeAddition * 15d, this.undigestedSugar);
                    this.undigestedSugar -= digest;
                    this.storedSugar += digest;
                    if (this.energyBalance > 0d) this.energyBalance += digest;
                    else this.energyBalance += 20d * digest;
                    this.bloodSugar -= 5d * this.digestDifficulty + 2.5d;
                } else this.undigestedSugar = 0d;
                if (this.undigestedFat > 0d) {
                    double digest = Math.min((Math.sqrt(Math.max(Math.min(this.cellulose, 4000d), 0d)) * 1.25d + 15d) * (1d - this.digestDifficulty) + cakeAddition * 6d, this.undigestedFat);
                    this.undigestedFat -= digest;
                    this.storedFat += digest;
                    if (this.energyBalance > 0d) this.energyBalance += 2.4d * digest;
                    else this.energyBalance += 48d * digest;
                    this.bloodSugar -= 5d * this.digestDifficulty + 2.5d;
                } else this.undigestedFat = 0d;
                if (this.undigestedProtein > 0d) {
                    double digest = Math.min((Math.sqrt(Math.max(Math.min(this.cellulose, 4000d), 0d)) * 2d + 30d) * (1d - this.digestDifficulty) + cakeAddition * 12d, this.undigestedProtein);
                    this.undigestedProtein -= digest;
                    this.protein += digest;
                    if (this.energyBalance > 0d) this.energyBalance += 0.9d * digest;
                    else this.energyBalance += 18d * digest;
                    this.bloodSugar -= 5d * this.digestDifficulty + 2.5d;
                } else this.undigestedProtein = 0d;
                if (this.energyBalance > Math.max(this.storedSugar, 1200000d))
                    this.energyBalance = Math.max(this.storedSugar, 1200000d);
                if (this.getStomachDryContains() < 1d) this.digestDifficulty = 0d;
                if (difficulty != Difficulty.PEACEFUL && this.bloodSugar < 0d) {
                    if (this.storedSugar > 50000d && !player.isDead()) {
                        player.damage(CustomDamageSources.HYPOGLYCEMIA, Float.MAX_VALUE);
                        if (player.isDead()) {
                            nutritionEffects.dieFromHypoglycemia = true;
                        }
                    } else if (!player.isDead()) {
                        player.damage(CustomDamageSources.HYPOGLYCEMIA_EXHAUSTED, Float.MAX_VALUE);
                        if (player.isDead()) {
                            nutritionEffects.dieFromHypoglycemiaExhausted = true;
                        }
                    }
                }
                double maxBloodSugar = 180d + this.storedSugar / 150d + this.muscle / 13333.3333d;
                if (this.bloodSugar < maxBloodSugar) {
                    this.storedSugar -= maxBloodSugar - this.bloodSugar;
                    this.energyBalance -= maxBloodSugar - this.bloodSugar;
                    this.bloodSugar = maxBloodSugar;
                }
                double maxMuscle = this.exerciseHistory < 50000d ? (-960000d * Math.pow(this.exerciseHistory / 14400d, 2) + 7800000d * this.exerciseHistory / 14400d + 1160000) : (78.7d * this.exerciseHistory + 12734259.26d);
                this.muscle -= (maxMuscle + this.muscle) / 3200000d;
                this.storedSugar += (maxMuscle + this.muscle) / 3555555.556d;
                double muscleGrowth = maxMuscle - this.muscle;
                double proteinGrowth = this.protein > 4000000d ? 0d : Math.pow((4000000d - this.protein) / 333.333d, 2);
                if (muscleGrowth > 0d && muscleGrowth > proteinGrowth) {
                    double exchange = Math.min(muscleGrowth - proteinGrowth, (muscleGrowth - proteinGrowth) / 60000d + 8d);
                    this.protein -= exchange;
                    this.muscle += exchange;
                } else if (proteinGrowth > 0d && muscleGrowth < proteinGrowth) {
                    double exchange = Math.min(proteinGrowth - muscleGrowth, (proteinGrowth - muscleGrowth) / 60000d + 25d);
                    this.muscle -= exchange;
                    this.protein += exchange;
                }
                if (this.protein > 4000000d) {
                    double exchange = Math.min(this.protein - 4000000d, (this.protein - 4000000d) / 6000d + 30d);
                    this.protein -= exchange;
                    this.storedSugar += 0.9d * exchange;
                }
                double starveLevel = this.getStarveLevel();
                double maxStoredSugar = 100000d + (1d - starveLevel) * (Math.min(400000d, (Math.min(200000d, this.iodine * 1000d) + Math.min(250000d, (this.muscle + Math.min(4000000d, this.protein)) / 60d))));
                if (this.storedSugar > maxStoredSugar) {
                    double exchange = Math.min(this.storedSugar - maxStoredSugar, (this.storedSugar - maxStoredSugar) / 1000d + 30d);
                    this.storedSugar -= exchange;
                    this.bloodFat += exchange / 2.4d;
                } else {
                    if (starveLevel > 0d) {
                        double exchange = starveLevel * Math.min((maxStoredSugar - this.storedSugar) / 6000d, (this.muscle + this.protein) / 150000d);
                        this.protein -= exchange / 0.9d;
                        this.storedSugar += exchange;
                    }
                    double exchange = Math.min(this.bloodFat, Math.min(1.2d, Math.sqrt(Math.max(this.iodine + 200d, 200d) / 350d)) * this.bloodFat / 510d + 10d);
                    this.bloodFat -= exchange;
                    this.storedSugar += 2.4d * exchange;
                }
                double maxBloodFat = Math.min(1d, 1.1d - starveLevel) * Math.max(0d, Math.atan(this.storedFat / 8000000d - 0.375d) * 17830d + 6400d);
                if (this.bloodFat > maxBloodFat) {
                    double exchange = Math.min(this.bloodFat - maxBloodFat, (this.bloodFat - maxBloodFat) / 100d + 10d);
                    this.bloodFat -= exchange;
                    this.storedFat += exchange;
                } else {
                    double exchange = Math.min(this.storedFat, (maxBloodFat - this.bloodFat) / 50d + 25d);
                    this.storedFat -= exchange;
                    this.bloodFat += exchange;
                }
                if (this.weakTime > 0) {
                    this.weakTime -= 1;
                    nutritionEffects.isWeak = true;
                }
                double waterConsume = (Math.exp((this.getWaterPercent()-60)/9d)*80d+20d)*(this.getBodyWeight()/60000000d);
                this.water -= waterConsume;
                this.heavyMetal -= Math.min(this.heavyMetal, waterConsume / 400d);
                this.vitaminA -= Math.min(this.vitaminA, this.vitaminA / 480000d + waterConsume / 60000d);
                this.vitaminB -= Math.min(this.vitaminB, this.vitaminB / 240000d + waterConsume / 200000d);
                this.vitaminC -= Math.min(this.vitaminC, this.vitaminC / 400000d + waterConsume / 40000d);
                this.iodine -= Math.min(this.iodine, this.iodine / 1500000d + waterConsume / 1000000d + (this.iodine > 250 ? Math.pow(this.iodine - 250d, 2d) / 1500000d : 0d));
                this.foodLevel = Math.min(20, Math.max(0, (int) (this.storedSugar / FOOD_LEVEL_EQUALS_SUGAR)));
                this.saturationLevel = Math.min(20f, Math.max(0f, (float) this.energyBalance / 60000f));
                double waterPercent = this.getWaterPercent();
                if (waterPercent > 63.6d) {
                    player.addStatusEffect(new StatusEffectInstance(StatusEffects.WEAKNESS, 200, 0));
                    nutritionEffects.isWaterIntoxicated = true;
                    if (Math.random() * waterPercent > 66d && !player.isDead()) {
                        player.damage(CustomDamageSources.WATER_INTOXICATION, 1f);
                        if (player.isDead()) {
                            nutritionEffects.dieFromWaterIntoxication = true;
                        }
                    }
                } else if (waterPercent < 54d) {
                    player.addStatusEffect(new StatusEffectInstance(StatusEffects.WEAKNESS, 200, 0));
                    nutritionEffects.isThirsty = true;
                    if (Math.random() * 48d > waterPercent && !player.isDead()) {
                        player.damage(CustomDamageSources.THIRSTY, 1f);
                        if (player.isDead()) {
                            nutritionEffects.dieFromThirsty = true;
                        }
                    }
                }
                if (this.heavyMetal/this.getWeight() > 0.00006d) {
                    if (!player.hasStatusEffect(StatusEffects.POISON)) {
                        if (player.getWorld().getTime() % 400 < 20)
                            player.addStatusEffect(new StatusEffectInstance(StatusEffects.POISON, 30, 0));
                        else player.addStatusEffect(new StatusEffectInstance(StatusEffects.POISON, 10, 0));
                    }
                    player.addStatusEffect(new StatusEffectInstance(StatusEffects.NAUSEA, 200, 0));
                    nutritionEffects.isPoisonedByHeavyMetal = true;
                }
                if (this.vitaminA < 100d) {
                    if (player.getWorld().getLightingProvider().getLight(player.getBlockPos(), player.getWorld().getAmbientDarkness()) < 7)
                        player.addStatusEffect(new StatusEffectInstance(StatusEffects.DARKNESS, 100, 0));
                    nutritionEffects.isLackInVA = true;
                }
                if (this.vitaminC < 100d) {
                    if (Math.random() < 0.000125d && !player.isDead())
                        player.damage(CustomDamageSources.BLEEDING, 1f);
                    nutritionEffects.isLackInVC = true;
                }
                int baseHealth = (int) (Math.min(20, player.experienceLevel / 3) + Math.min(20, Math.max(2, (this.muscle + this.protein + this.storedFat) / 2000000 - 2)));
                double moveAbility = Math.max(0.025d, 0.27386d * Math.sqrt(Math.pow(this.getMuscleEffect(), 2d) * 8000000d / this.getWeight()));
                double attackPower = this.getMuscleEffect();
                double attackPowerMultiplier = this.getMuscleEffect();
                if (attackPower < 0.3d) {
                    nutritionEffects.isLackInStrength = true;
                } else if (attackPower > 1.5d) {
                    nutritionEffects.isPowerful = true;
                }
                Objects.requireNonNull(player.getAttributeInstance(EntityAttributes.GENERIC_MAX_HEALTH)).setBaseValue(baseHealth);
                if (player.getHealth() > player.getMaxHealth()) player.setHealth(player.getMaxHealth());
                Objects.requireNonNull(player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)).setBaseValue(moveAbility);
                Objects.requireNonNull(player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE)).setBaseValue(attackPower);
                Objects.requireNonNull(player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE)).removeModifier(TEMP_ATTACK_DAMAGE_ID);
                Objects.requireNonNull(player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE)).addTemporaryModifier(new EntityAttributeModifier(TEMP_ATTACK_DAMAGE_ID, "muscle power", attackPowerMultiplier, EntityAttributeModifier.Operation.MULTIPLY_TOTAL));
                if (starveLevel > 0.5d) {
                    ++this.foodTickTimer;
                    if (this.foodTickTimer >= 2000 * (1 - starveLevel)) {
                        if ((player.getHealth() > 10f || difficulty == Difficulty.HARD || (player.getHealth() > 1f && difficulty == Difficulty.NORMAL)) && !player.isDead()) {
                            player.damage(DamageSource.STARVE, 1f);
                            if (player.isDead()) {
                                nutritionEffects.dieFromStarve = true;
                            }
                        }
                        this.foodTickTimer = 0;
                    }
                    nutritionEffects.isStarved = true;
                } else {
                    this.foodTickTimer = 0;
                    if (player.canFoodHeal()) {
                        if (difficulty == Difficulty.PEACEFUL || (player.getSleepTimer() == 100 && this.vitaminB > 20d && this.vitaminC > 100d && this.storedSugar > 225000d && this.protein > 3200000d)) {
                            player.heal(1f / 500f);
                            if (difficulty != Difficulty.PEACEFUL) {
                                this.storedSugar -= 165d;
                                this.protein -= 50d;
                                this.energyBalance -= 200d;
                            }
                        } else if (player.getSleepTimer() == 100) {
                            nutritionEffects.cannotRegenerate = true;
                        }
                    }
                }
                if (starveLevel > 0) {
                    nutritionEffects.isHungry = true;
                }
                if (this.storedSugar < 25000d) {
                    nutritionEffects.isExhausted = true;
                }
                if (Math.sqrt(Math.max(Math.min(this.cellulose,4000d),0d))*(1d-this.digestDifficulty)+cakeAddition < 10d) {
                    nutritionEffects.isDyspepsia = true;
                }
                if (this.vitaminB < 20d) {
                    nutritionEffects.isLackInVB = true;
                }
                if (this.iodine < 10d) {
                    nutritionEffects.isLackInIodine = true;
                } else if (this.iodine > 600d) {
                    nutritionEffects.isIodineExcess = true;
                }
                nutritionEffects.fats = (float) (this.storedFat / 1000000d);
                nutritionEffects.muscles = (float) (this.muscle / 1000000d);
                nutritionEffects.weight = (float) (this.getWeight() / 1000000d);
                LishCriteria.NUTRITION_EFFECTS.trigger((ServerPlayerEntity)player, nutritionEffects);
            } else {
                Objects.requireNonNull(player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)).setBaseValue(0.1d);
            }
        }
    }

    @Override
    public void readNbt(NbtCompound nbt) {
        if (nbt.contains("foodLevel", 99)) {
            this.foodLevel = nbt.getInt("foodLevel");
            this.foodTickTimer = nbt.getInt("foodTickTimer");
            this.saturationLevel = nbt.getFloat("foodSaturationLevel");
            this.exhaustion = nbt.getFloat("foodExhaustionLevel");
            NbtCompound nutrients = nbt.getCompound(NUTRIENTS);
            if (nutrients != null) {
                this.applyUpdate = nutrients.getBoolean(APPLY_UPDATE);
                this.isInputting = nutrients.getBoolean(IS_INPUTTING);
                this.weakTime = nutrients.getInt(WEAK_TIME);
                this.energyBalance = nutrients.getDouble(ENERGY_BALANCE);
                this.exerciseHistory = nutrients.getDouble(EXERCISE_HISTORY);
                this.digestDifficulty = nutrients.getDouble(DIGEST_DIFFICULTY);
                this.water = nutrients.getDouble(WATER);
                this.undigestedWater = nutrients.getDouble(UNDIGESTED_WATER);
                this.heavyMetal = nutrients.getDouble(HEAVY_METAL);
                this.bloodSugar = nutrients.getDouble(BLOOD_SUGAR);
                this.storedSugar = nutrients.getDouble(STORED_SUGAR);
                this.undigestedSugar = nutrients.getDouble(UNDIGESTED_SUGAR);
                this.bloodFat = nutrients.getDouble(BLOOD_FAT);
                this.storedFat = nutrients.getDouble(STORED_FAT);
                this.undigestedFat = nutrients.getDouble(UNDIGESTED_FAT);
                this.muscle = nutrients.getDouble(MUSCLE);
                this.protein = nutrients.getDouble(PROTEIN);
                this.undigestedProtein = nutrients.getDouble(UNDIGESTED_PROTEIN);
                this.cellulose = nutrients.getDouble(CELLULOSE);
                this.vitaminA = nutrients.getDouble(VA);
                this.vitaminB = nutrients.getDouble(VB);
                this.vitaminC = nutrients.getDouble(VC);
                this.iodine = nutrients.getDouble(IODINE);
            }
        }
    }

    @Override
    public void writeNbt(NbtCompound nbt) {
        nbt.putInt("foodLevel", this.foodLevel);
        nbt.putInt("foodTickTimer", this.foodTickTimer);
        nbt.putFloat("foodSaturationLevel", this.saturationLevel);
        nbt.putFloat("foodExhaustionLevel", this.exhaustion);
        NbtCompound nutrients = new NbtCompound();
        nutrients.putBoolean(APPLY_UPDATE,this.applyUpdate);
        nutrients.putBoolean(IS_INPUTTING,this.isInputting);
        nutrients.putInt(WEAK_TIME,this.weakTime);
        nutrients.putDouble(ENERGY_BALANCE, this.energyBalance);
        nutrients.putDouble(EXERCISE_HISTORY, this.exerciseHistory);
        nutrients.putDouble(DIGEST_DIFFICULTY,this.digestDifficulty);
        nutrients.putDouble(WATER, this.water);
        nutrients.putDouble(UNDIGESTED_WATER, this.undigestedWater);
        nutrients.putDouble(HEAVY_METAL, this.heavyMetal);
        nutrients.putDouble(BLOOD_SUGAR, this.bloodSugar);
        nutrients.putDouble(STORED_SUGAR, this.storedSugar);
        nutrients.putDouble(UNDIGESTED_SUGAR, this.undigestedSugar);
        nutrients.putDouble(BLOOD_FAT, this.bloodFat);
        nutrients.putDouble(STORED_FAT, this.storedFat);
        nutrients.putDouble(UNDIGESTED_FAT, this.undigestedFat);
        nutrients.putDouble(MUSCLE, this.muscle);
        nutrients.putDouble(PROTEIN, this.protein);
        nutrients.putDouble(UNDIGESTED_PROTEIN, this.undigestedProtein);
        nutrients.putDouble(CELLULOSE, this.cellulose);
        nutrients.putDouble(VA, this.vitaminA);
        nutrients.putDouble(VB, this.vitaminB);
        nutrients.putDouble(VC, this.vitaminC);
        nutrients.putDouble(IODINE, this.iodine);
        nbt.put(NUTRIENTS , nutrients);
    }

    public void reSet(){
        this.weakTime = 0;
        this.exhaustion = 0f;
        this.heavyMetal = 0d;
        this.bloodSugar = 3780d;
        this.storedSugar = 450000d;
        this.bloodFat = 21450d;
        this.digestDifficulty = 0d;
        this.undigestedWater = 0d;
        this.undigestedSugar = 0d;
        this.undigestedFat = 0d;
        this.undigestedProtein = 0d;
        if (this.energyBalance < 0d) this.energyBalance = 0d;
        if (this.storedFat < 3000000d) this.storedFat = 3000000d;
        if (this.protein < 3000000d) this.protein = 3000000d;
        if (this.muscle < 2000000d) this.muscle = 2000000d;
        this.water = (this.getBodyWeight()-this.water)*1.5d;
    }

    public void lostEnergy(float energy){
        if (energy > 0d && this.applyUpdate){
            this.storedSugar -= SUGAR_HEAT_RECIPROCAL * energy;
            this.energyBalance -= SUGAR_HEAT_RECIPROCAL * energy;
        }
    }

    public void diarrhea(int amplifier){
        if (this.applyUpdate) {
            this.undigestedSugar -= Math.min(this.undigestedSugar, (1f + 0.2f * amplifier) * (this.undigestedWater / 1000d + 10d));
            this.undigestedFat -= Math.min(this.undigestedFat, (1f + 0.2f * amplifier) * (this.undigestedFat / 1000d + 10d));
            this.undigestedProtein -= Math.min(this.undigestedProtein, (1f + 0.2f * amplifier) * (this.undigestedProtein / 1000d + 10d));
            double waterLoss = Math.min(this.undigestedWater, (1f + 0.2f * amplifier) * (this.undigestedWater / 300d + 120d));
            this.undigestedWater -= waterLoss;
            if (waterLoss < (1f + 0.2f * amplifier) * 1200d) this.water -= (1f + 0.2f * amplifier) * 1200d - waterLoss;
            if (this.weakTime < 5)this.weakTime = 5;
            this.weakTime += 1+2*(amplifier+1)*Math.random();
        }
    }

    public double getStarveLevel(){
        return this.energyBalance > 0d? 0d : this.energyBalance/(this.energyBalance-2400000d);
    }

    public int getHeavyMetal(){
        return (int) this.heavyMetal;
    }

    public int getBloodSugar(){
        return (int) this.bloodSugar;
    }

    public int getBloodFat(){
        return (int) this.bloodFat;
    }

    public int getVA(){
        return (int) this.vitaminA;
    }

    public int getVB(){
        return (int) this.vitaminB;
    }

    public int getVC(){
        return (int) this.vitaminC;
    }

    public int getIodine(){
        return (int) this.iodine;
    }

    public int getFat(){
        return (int) this.storedFat;
    }

    public int getMuscle(){
        return (int) this.muscle;
    }

    public double getMuscleEffect(){
        return Math.sqrt(this.muscle/8000000d)*Math.min(1d,0.3d+this.vitaminB/30d)*Math.max(0.6d,Math.min(1d,1.5d-this.iodine/1200d))*(this.weakTime>0? 0.8d:1d);
    }

    public double getWeight(){
        return this.water+this.storedSugar+this.storedFat+this.muscle+this.protein+this.getStomachContains();
    }

    public double getBodyWeight(){
        return this.water+this.storedSugar+this.storedFat+this.muscle+this.protein;
    }

    public double getWaterPercent(){
        return 100d*this.water/this.getBodyWeight();
    }

    public double getStomachContains(){
        return this.undigestedWater+this.getStomachDryContains();
    }

    public double getStomachDryContains(){
        return this.undigestedSugar+this.undigestedFat+this.undigestedProtein;
    }

    public void removeHeavyMetal(double amount){
        if (this.heavyMetal > 0 && amount > 0f && this.applyUpdate) this.heavyMetal -= Math.min(this.heavyMetal/3d,amount);
    }

    public void reallyAddExhaustion(float exhaustion,boolean needInput){
        if (exhaustion > 0f && this.applyUpdate && (!needInput || this.isInputting))this.exhaustion += exhaustion;
    }

    @Override
    public int getFoodLevel() {
        return this.foodLevel;
    }

    @Override
    public int getPrevFoodLevel() {
        return this.prevFoodLevel;
    }

    @Override
    public boolean isNotFull() {
        return this.getStomachContains() < 1000000d;
    }

    @Override
    public void addExhaustion(float exhaustion) {
    }

    @Override
    public float getExhaustion() {
        return this.exhaustion;
    }

    @Override
    public float getSaturationLevel() {
        return this.saturationLevel;
    }

    @Override
    public void setFoodLevel(int foodLevel) {
        if (this.applyUpdate){
            foodLevel = Math.min(Math.max(0 , foodLevel) , 20);
            this.energyBalance -= this.storedSugar - (foodLevel * FOOD_LEVEL_EQUALS_SUGAR);
            this.storedSugar = foodLevel * FOOD_LEVEL_EQUALS_SUGAR;
            this.foodLevel = foodLevel;
        }
    }

    @Override
    public void setSaturationLevel(float saturationLevel) {
        if (this.applyUpdate){
            saturationLevel = Math.min(Math.max(0f , saturationLevel) , 20f);
            this.energyBalance = 60000d * saturationLevel;
            this.saturationLevel = saturationLevel;
        }
    }

    @Override
    public void setExhaustion(float exhaustion) {
        if (this.applyUpdate && exhaustion >= 0f)this.exhaustion = exhaustion;
    }

    public static class NutritionEffects {
        public boolean isWeak = false;
        public boolean isHungry = false;
        public boolean isStarved = false;
        public boolean isFull = false;
        public boolean isEmptyStomach = false;
        public boolean isThirsty = false;
        public boolean isWaterIntoxicated = false;
        public boolean isPoisonedByHeavyMetal = false;
        public boolean isExhausted = false;
        public boolean isDyspepsia = false;
        public boolean isLackInVA = false;
        public boolean isLackInVB = false;
        public boolean isLackInVC = false;
        public boolean isLackInIodine = false;
        public boolean isIodineExcess = false;
        public boolean isLackInStrength = false;
        public boolean isPowerful = false;
        public boolean cannotRegenerate = false;
        public boolean dieFromThirsty = false;
        public boolean dieFromWaterIntoxication = false;
        public boolean dieFromStomachRupture = false;
        public boolean dieFromGluttony = false;
        public boolean dieFromHypoglycemia = false;
        public boolean dieFromHypoglycemiaExhausted = false;
        public boolean dieFromStarve = false;
        public float fats = 0f;
        public float muscles = 0f;
        public float weight = 0f;
    }
}
